﻿#-------------------------------------------------------------------------------
# Name:        storepacket.py
# Purpose:      封包解释类
#
# Author:      seewind
#
# Created:     25-02-2008
# Copyright:   (c) seewind 2008
# Licence:     <your licence>
#-------------------------------------------------------------------------------
#!/usr/bin/env python

import WebStore.tools.dpkt as dpkt
import cStringIO as StringIO
import struct

#数据类型
TYPE_NONE = 0
TYPE_CS_LOGIN = 1               #登陆
TYPE_CS_REQUEST_URL = 2         #请求URL资源
TYPE_CS_RESPONSE_URLLIST = 3    #查询真实url的返回数据

TYPE_SC_RESPONSE_LOGIN = 11     #回应登陆
TYPE_SC_URLPIECES = 12          #返回URL可下载块列表
TYPE_SC_SAVE_URLPIECES = 13     #主动要求客户下载保存某些块
TYPE_SC_REQUEST_URLLIST = 14    #通过UrlHash值，查询真实的url

TYPE_CC_REQUEST_URLPIECES = 21  #客户端发送请求块信息
TYPE_CC_RESPONSE_PIECE = 22     #客户端返回块内容


#常量
FMT_UrlCost = 'I'
FMT_Count = 'H'
FMT_UrlHash = '20s'
FMT_Pieces = '256s'
FMT_PieceRange = 'H'
FMT_PieceID = 'H'
FMT_PeerID = '20s'

LEN_UrlHash = 20
LEN_Pieces = 256
LEN_PeerID = 20

DEFAULT_byte_order = '='

CURRENT_VERSION = 1

def unpack_count(pobj):
    """ 解释count列表方式的结构 """
    ifmt = pobj._count_hdr.join([i and '' for i in range(1, pobj.count+2)])
    #返回list,是为了允许修改其内容
    return list(struct.unpack(DEFAULT_byte_order + ifmt, pobj.data))

def pack_count(pobj):
    """ 组装count列表方式的结构 """
    ifmt = pobj._count_hdr.join([i and '' for i in range(1, pobj.count+2)])
    return struct.pack(DEFAULT_byte_order + ifmt, *pobj.data)


class BasePacket(dpkt.Packet):
    __byte_order__ = DEFAULT_byte_order
    
class subBasePacket(BasePacket):
    @classmethod
    def packettype(cls):
        global typesw_values, typesw_keys
        return typesw_keys[typesw_values.index(cls)]
    

class CS_LOGIN(subBasePacket):
    """ 登陆后，发送客户保存的块列表:cacheSize(int)+Count+(UrlHash+PieceRange)列表 """
    __hdr__ = (('cachesize', 'I', 0), ('count', FMT_Count, 0))
    _count_hdr = FMT_UrlHash+FMT_PieceRange+FMT_PieceRange

    def unpack(self, buf):
        dpkt.Packet.unpack(self, buf)
        self.data = unpack_count(self)
        setattr(self, 'urlhash_piecerange_list', self.data)

    def __str__(self):
        return self.pack_hdr() + pack_count(self, self.urlhash_piecerange_list)


class CS_REQUEST_URL(subBasePacket):
    """ 请求某url可用的下载块列表:UrlHash+PieceRange """
    __hdr__ = (('urlhash', FMT_UrlHash, ''),
        ('piecestart', FMT_PieceRange, 0), ('pieceend', FMT_PieceRange, 0))


class CS_RESPONSE_URLLIST(subBasePacket):
    """ 查询真实url的返回数据:(UrlHash+url+回车符(\n))列表 """
    def unpack(self, buf):
        sio = StringIO.StringIO(buf)
        ilist = []
        for s in sio.readlines:
            ilist.append((s[0:LEN_UrlHash], s[LEN_UrlHash:-1]))
        self.data = ilist
        setattr(self, 'urldm5_url_list', self.data)

    def __str__(self):
        if not getattr(self, 'urldm5_url_list', None):
            return ''
        ilist = []
        for t in self.urldm5_url_list:
            ilist.extend(t)
            ilist.append('\n')
        return ''.join(ilist)
    
    
class SC_RESPONSE_LOGIN(subBasePacket):
    """ 发送客户端登陆回应信息:Count+(UrlHash+urlCost)列表 """
    __hdr__ = (('count', FMT_Count, 0), )
    _count_hdr = FMT_UrlHash + FMT_UrlCost
    def unpack(self, buf):
        dpkt.Packet.unpack(self, buf)
        self.data = unpack_count(self)
        setattr(self, 'urlhash_urlcost_list', self.data)

    def __str__(self):
        if not self.data:
            self.data = getattr(self, 'urlhash_urlcost_list', None)
        return self.pack_hdr() + pack_count(self)

class SC_URLPIECES(subBasePacket):
    """ 发送某url可用的下载块列表:UrlHash+UrlCost+PieceRange+Count+PeerID列表 """
    __hdr__ = (('urlhash', FMT_UrlHash, ''), ('urlcost', FMT_UrlCost, 0),
        ('piecestart', FMT_PieceRange, 0), ('pieceend', FMT_PieceRange, 0),
        ('count', FMT_Count, 0))
    _count_hdr = FMT_PeerID
    
    def unpack(self, buf):
        dpkt.Packet.unpack(self, buf)
        self.data = unpack_count(self)
        setattr(self, 'peerid_list', self.data)
        
    def __str__(self):
        if not self.data:
            self.data = getattr(self, 'peerid_list', None)
        return self.pack_hdr() + pack_count(self)
    
class SC_SAVE_URLPIECES(subBasePacket):
    """ 主动要求客户下载保存某些块:UrlCost+PieceRange+Url """
    __hdr__ = (('urlcost', FMT_UrlCost, 0),
        ('piecestart', FMT_PieceRange, 0), ('pieceend', FMT_PieceRange, 0))
    
    def unpack(self, buf):
        dpkt.Packet.unpack(self, buf)
        setattr(self, 'url', self.data)

    def __str__(self):
        return self.pack_hdr() + self.url

class SC_REQUEST_URLLIST(subBasePacket):
    """ 通过UrlHash值，查询真实的url:Count+UrlHash列表 """
    __hdr__ = (('count', FMT_Count, 0), )
    _count_hdr = FMT_UrlHash
    
    def unpack(self, buf):
        dpkt.Packet.unpack(self, buf)
        self.data = unpack_count(self)
        setattr(self, 'urlhash_list', self.data)
        
    def __str__(self):
        if not self.data:
            self.data = getattr(self, 'urlhash_list', None)
        return self.pack_hdr() + pack_count(self)
    

class CC_REQUEST_URLPIECES(subBasePacket):
    """ 客户端发送请求块信息:UrlHash+UrlCost+PieceRange """
    __hdr__ = (('urlhash', FMT_UrlHash, ''), ('urlcost', FMT_UrlCost, 0),
        ('piecestart', FMT_PieceRange, 0), ('pieceend', FMT_PieceRange, 0))

class CC_RESPONSE_PIECE(subBasePacket):
    """ 客户端返回块内容:UrlHash+PieceID+Data """
    __hdr__ = (('urlhash', FMT_UrlHash, ''), ('pieceid', FMT_PieceID, 0))
    
    

typesw = {  TYPE_NONE:None,
            TYPE_CS_LOGIN:              CS_LOGIN,
            TYPE_CS_REQUEST_URL:        CS_REQUEST_URL,
            TYPE_CS_RESPONSE_URLLIST:   CS_RESPONSE_URLLIST,
            
            TYPE_SC_RESPONSE_LOGIN:     SC_RESPONSE_LOGIN,
            TYPE_SC_URLPIECES:          SC_URLPIECES,
            TYPE_SC_SAVE_URLPIECES:     SC_SAVE_URLPIECES,
            TYPE_SC_REQUEST_URLLIST:    SC_REQUEST_URLLIST,
            
            TYPE_CC_REQUEST_URLPIECES:  CC_REQUEST_URLPIECES,
            TYPE_CC_RESPONSE_PIECE:     CC_RESPONSE_PIECE
        }
        
typesw_keys = typesw.keys()
typesw_values = typesw.values()

class StorePacket(BasePacket):
    """ 封包解释类 """
    __hdr__ = ( ('ver', 'B', 1), ('type', 'B', 0) )

    def unpack(self, buf):
        global typesw
        
        dpkt.Packet.unpack(self, buf)
        try:
            self.data = typesw[self.type](self.data)
            setattr(self, self.data.__class__.__name__.upper(), self.data)
        except (KeyError, dpkt.PackError):
            self.data = buf
            
    def __str__(self):
        return dpkt.Packet.__str__(self)
            

def makepacket(ptype, md5str=None):
    if not md5str:
        import md5
        md5str = md5.new('test').digest()
        
    if ptype == TYPE_CS_LOGIN:
        return struct.pack(DEFAULT_byte_order + 'BBIH'+ FMT_UrlHash + 'HH',
            1, TYPE_CS_LOGIN, 898, 1, md5str, 2, 9)
    elif ptype == TYPE_CS_REQUEST_URL:
        return struct.pack(DEFAULT_byte_order + 'BB'+FMT_UrlHash + 'HH',
            1, TYPE_CS_REQUEST_URL, md5str, 2, 9)

def test():
    #测试 TYPE_CS_LOGIN
    idata = makepacket(TYPE_CS_LOGIN)
    sp = StorePacket(idata)
    assert isinstance(sp.data, CS_LOGIN)

    # TYPE_CS_REQUEST_URL
    idata = makepacket(TYPE_CS_REQUEST_URL)
    sp = StorePacket(idata)
    assert isinstance(sp.data, CS_REQUEST_URL)

if __name__ == '__main__':
    test()
    
    
